﻿////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// <copyright>Copyright 2008-2011 Andreas Huber Doenni</copyright>
// Distributed under the GNU General Public License version 2 (GPLv2).
// See accompanying file License.txt or copy at http://phuse.codeplex.com/license.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Phuse.Net.Forums
{
    using System;
    using System.Diagnostics.CodeAnalysis;

    internal partial class SendReceiveHelper<TConnection> where TConnection : Connection
    {
        private abstract class OperationContext : IDisposable
        {
            private readonly SendReceiveContext context;
            private readonly IProgress<ISendReceiveProgress> progress;
            private Database database;
            private Group group;
            private Root root;
            private long? itemIndex;
            private int currentPercentage;

            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

            /// <summary>See <see cref="IDisposable.Dispose"/>.</summary>
            public void Dispose()
            {
                this.Dispose(true);
            }

            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

            internal OperationContext(SendReceiveContext context, IProgress<ISendReceiveProgress> progress)
            {
                this.context = context;
                this.progress = progress;
            }

            internal Group Group
            {
                get
                {
                    this.EnsureDatabaseOpen();
                    return this.group;
                }
            }

            internal Root Root
            {
                get
                {
                    this.EnsureDatabaseOpen();
                    return this.root;
                }
            }

            internal long ItemIndex
            {
                get
                {
                    return this.itemIndex ?? this.ItemOffset;
                }

                set
                {
                    this.itemIndex = value;
                    this.CheckReport();
                }
            }

            internal bool StartOperation()
            {
                var result = this.ItemCount > 0;

                if (result)
                {
                    this.Report();
                }

                return result;
            }

            internal virtual void Submit(int insertCount = 0)
            {
                if (this.database != null)
                {
                    this.Root.InsertsAndDeletesSinceLastCompaction += insertCount;
                    this.Root.SubmitChanges();
                    this.CloseDatabase();
                }
            }

            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

            [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Dispose() must never throw.")]
            protected virtual void Dispose(bool disposing)
            {
                try
                {
                    if (disposing)
                    {
                        this.CloseDatabase();
                    }
                }
                catch
                {
                }
            }

            protected abstract long ItemOffset { get; }

            protected abstract long ItemCount { get; }

            protected abstract ISendReceiveProgress CreateProgress(int percent);

            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

            private void EnsureDatabaseOpen()
            {
                if (this.database == null)
                {
                    this.context.ReopenDatabase(ref this.database, out this.group, out this.root);
                }
            }

            private void CheckReport()
            {
                var percentage = (int)Math.Min((this.ItemIndex - this.ItemOffset) * 100 / this.ItemCount, 100L);

                if (percentage > this.currentPercentage)
                {
                    this.currentPercentage = percentage;
                    this.Report();
                }
            }

            private void Report()
            {
                this.progress.Report(this.CreateProgress(this.currentPercentage));
            }

            private void CloseDatabase()
            {
                try
                {
                    if (this.database != null)
                    {
                        this.database.Dispose();
                    }
                }
                finally
                {
                    this.database = null;
                    this.group = null;
                    this.root = null;
                }
            }
        }
    }
}
